import 'dart:convert';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import '../models/habit.dart';

class DatabaseService {
  static const String _dbName = 'habits_database.db';
  static const int _dbVersion = 1;
  static const String _habitsTable = 'habits';
  
  static Database? _database;
  
  // Singleton pattern
  DatabaseService._privateConstructor();
  static final DatabaseService instance = DatabaseService._privateConstructor();
  
  // Get database
  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDatabase();
    return _database!;
  }
  
  // Initialize database
  Future<Database> _initDatabase() async {
    final documentsDirectory = await getApplicationDocumentsDirectory();
    final path = join(documentsDirectory.path, _dbName);
    
    return await openDatabase(
      path,
      version: _dbVersion,
      onCreate: _onCreate,
    );
  }
  
  // Create tables
  Future<void> _onCreate(Database db, int version) async {
    await db.execute('''
      CREATE TABLE $_habitsTable(
        id TEXT PRIMARY KEY,
        name TEXT NOT NULL,
        description TEXT,
        icon TEXT,
        colorValue INTEGER,
        frequency TEXT,
        amount TEXT,
        completedDates TEXT,
        createdAt TEXT,
        reminderTimes TEXT
      )
    ''');
  }
  
  // Add a new habit
  Future<void> addHabit(Habit habit) async {
    final db = await database;
    
    await db.insert(
      _habitsTable,
      {
        'id': habit.id,
        'name': habit.name,
        'description': habit.description,
        'icon': habit.icon,
        'colorValue': habit.colorValue,
        'frequency': habit.frequency,
        'amount': habit.amount,
        'completedDates': jsonEncode(habit.completedDates.map((date) => date.toIso8601String()).toList()),
        'createdAt': habit.createdAt.toIso8601String(),
        'reminderTimes': jsonEncode(habit.reminderTimes),
      },
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }
  
  // Get all habits
  Future<List<Habit>> getHabits() async {
    final db = await database;
    
    final List<Map<String, dynamic>> maps = await db.query(_habitsTable);
    
    return List.generate(maps.length, (i) {
      return Habit(
        id: maps[i]['id'],
        name: maps[i]['name'],
        description: maps[i]['description'],
        icon: maps[i]['icon'],
        colorValue: maps[i]['colorValue'],
        frequency: maps[i]['frequency'],
        amount: maps[i]['amount'],
        completedDates: (jsonDecode(maps[i]['completedDates']) as List)
            .map((dateStr) => DateTime.parse(dateStr))
            .toList(),
        createdAt: DateTime.parse(maps[i]['createdAt']),
        reminderTimes: (jsonDecode(maps[i]['reminderTimes']) as List)
            .map((time) => time as String)
            .toList(),
      );
    });
  }
  
  // Get habit by ID
  Future<Habit?> getHabitById(String id) async {
    final db = await database;
    
    final List<Map<String, dynamic>> maps = await db.query(
      _habitsTable,
      where: 'id = ?',
      whereArgs: [id],
    );
    
    if (maps.isEmpty) return null;
    
    final map = maps.first;
    return Habit(
      id: map['id'],
      name: map['name'],
      description: map['description'],
      icon: map['icon'],
      colorValue: map['colorValue'],
      frequency: map['frequency'],
      amount: map['amount'],
      completedDates: (jsonDecode(map['completedDates']) as List)
          .map((dateStr) => DateTime.parse(dateStr))
          .toList(),
      createdAt: DateTime.parse(map['createdAt']),
      reminderTimes: (jsonDecode(map['reminderTimes']) as List)
          .map((time) => time as String)
          .toList(),
    );
  }
  
  // Update a habit
  Future<void> updateHabit(Habit habit) async {
    final db = await database;
    
    await db.update(
      _habitsTable,
      {
        'name': habit.name,
        'description': habit.description,
        'icon': habit.icon,
        'colorValue': habit.colorValue,
        'frequency': habit.frequency,
        'amount': habit.amount,
        'completedDates': jsonEncode(habit.completedDates.map((date) => date.toIso8601String()).toList()),
        'reminderTimes': jsonEncode(habit.reminderTimes),
      },
      where: 'id = ?',
      whereArgs: [habit.id],
    );
  }
  
  // Delete a habit
  Future<void> deleteHabit(String id) async {
    final db = await database;
    
    await db.delete(
      _habitsTable,
      where: 'id = ?',
      whereArgs: [id],
    );
  }
  
  // Toggle habit completion for today
  Future<Habit> toggleHabitCompletion(Habit habit) async {
    final now = DateTime.now();
    final today = DateTime(now.year, now.month, now.day);
    
    Habit updatedHabit;
    if (habit.isCompletedToday()) {
      updatedHabit = habit.removeCompletedDate(today);
    } else {
      updatedHabit = habit.addCompletedDate(today);
    }
    
    await updateHabit(updatedHabit);
    return updatedHabit;
  }
}
